﻿namespace BeatEmUp.World
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using BeatEmUp.Menu;
    using BeatEmUp.Characters.Enemies;
    using BeatEmUp.HUD;
    using BeatEmUp.Items.Weapons;
    using BeatEmUp.Enums;
    using BeatEmUp.World.Drawable;
    using Microsoft.Xna.Framework.Content;

    class ConfigBuilder<T,V>
        where T : Element
        where V : AbstractElementFactory<T>, new()
    {
        private GameManager gameManager;
        private List<T> elements = new List<T>();
        Random rand = new Random(666);

        public ConfigBuilder(GameManager gameManager)
        {
            this.gameManager = gameManager;
        }

        public ConfigBuilder<T,V> AddBasicElement(Vector2 absolutePosition, Drawable.IDrawable drawable, float depth)
        {
            T element = this.CreateElement(absolutePosition, drawable.Clone(), depth);
            this.elements.Add(element);

            return this;
        }

        public ConfigBuilder<T, V> AddHorizontalRepeatingElement(Vector2 absoluteStartingPosition, int clones, int spaceBetween, Drawable.IDrawable drawable, float depth)
        {
            Drawable.IDrawable[] drawables = new Drawable.IDrawable[1] { drawable };

            return this.AddHorizontalRepeatingElement(absoluteStartingPosition, clones, spaceBetween, drawables, depth);
        }

        public ConfigBuilder<T,V> AddHorizontalRepeatingElement(Vector2 absoluteStartingPosition, int clones, int spaceBetween, Drawable.IDrawable[] drawables, float depth)
        {
            absoluteStartingPosition = this.GetNextPosition(absoluteStartingPosition, -spaceBetween);

            for (int i = 0; i < clones; ++i)
            {
                absoluteStartingPosition = this.GetNextPosition(absoluteStartingPosition, spaceBetween);
                this.AddBasicElement(absoluteStartingPosition, this.GetRandomDrawable(drawables), depth);
            }

            return this;
        }

        public ConfigBuilder<T, V> AddHorizontalRepeatingElement(Vector2 absoluteStartingPosition, int clones, int spaceFrom, int spaceTo, Drawable.IDrawable[] drawables, float depth)
        {
            absoluteStartingPosition = this.GetNextPosition(absoluteStartingPosition, -spaceFrom);

            for (int i = 0; i < clones; ++i)
            {
                absoluteStartingPosition = this.GetRandomPosition(absoluteStartingPosition, spaceFrom, spaceTo);

                this.AddBasicElement(absoluteStartingPosition, this.GetRandomDrawable(drawables), depth);
            }

            return this;
        }

        public ConfigBuilder<T, V> AddRepeatingElement(Vector2 absoluteStartingPosition, int xClones, int xSpaceBetween, int yClones, int ySpaceBetween, Drawable.IDrawable drawable, float depth)
        {
            Drawable.IDrawable[] drawables = new Drawable.IDrawable[1] { drawable };

            return this.AddRepeatingElement(absoluteStartingPosition, xClones, xSpaceBetween, yClones, ySpaceBetween, drawables, depth);
        }

        public ConfigBuilder<T,V> AddRepeatingElement(Vector2 absoluteStartingPosition, int xClones, int xSpaceBetween, int yClones, int ySpaceBetween, Drawable.IDrawable[] drawables, float depth)
        {
            for (int i = 0; i < yClones; ++i)
            {
                this.AddHorizontalRepeatingElement(absoluteStartingPosition, xClones, xSpaceBetween, drawables, depth);
                absoluteStartingPosition.Y += ySpaceBetween;
            }

            return this;
        }

        public Config<T> GetConfig()
        {
            return new Config<T>(this.gameManager, this.elements.ToArray());
        }

        protected Vector2 GetNextPosition(Vector2 oldPosition, int spaceBetween)
        {
            return new Vector2(oldPosition.X + spaceBetween, oldPosition.Y);
        }

        protected Vector2 GetRandomPosition(Vector2 basePosition, int spaceFrom, int spaceTo)
        {
            return new Vector2(basePosition.X + this.rand.Next(spaceFrom, spaceTo), basePosition.Y);
        }

        protected Drawable.IDrawable GetRandomDrawable(Drawable.IDrawable[] drawables)
        {
            return drawables[ this.rand.Next(drawables.Count()) ];
        }

        protected T CreateElement(Vector2 absolutePosition, Drawable.IDrawable drawable, float depth)
        {
            V factory = new V();
            factory.gameManager = this.gameManager;
            factory.absolutePosition = absolutePosition;
            factory.drawable = drawable;
            factory.depth = depth;

            return factory.Build();
        }
    }


    public abstract class AbstractElementFactory<T> where T: Element
    {
        public GameManager gameManager;
        public Vector2 absolutePosition;
        public Drawable.IDrawable drawable;
        public float depth;

        public abstract T Build();
    }

    public class ElementFactory : AbstractElementFactory<Element>
    {
        public override Element Build()
        {
            return new Element(this.gameManager, this.absolutePosition, this.drawable, this.depth);
        }
    }

    public class StaticElementFactory : AbstractElementFactory<StaticElement>
    {
        public override StaticElement Build()
        {
            return new StaticElement(this.gameManager, this.absolutePosition, this.drawable, this.depth);
        }
    }
}
